/*	--*- c -*--
 * Copyright (C) 2016 Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdbool.h>
#include "bayer2rgb.h"
#include "bayer2rgb-internal.h"

static inline unsigned int avg1(unsigned int max, int sft, unsigned int a)
{
	unsigned int		res = a;

	if (sft) {
		res += (1u << (sft-1));
		res >>= sft;
	}

	if (sft && res > max)
		res = max;

	return res;
}

static inline unsigned int avg2(bool do_round, unsigned int max, int sft,
				unsigned int a)
{
	unsigned int		res;

	if (!do_round)
		res = a / 2;
	else
		res = (a + 1) / 2;

	if (sft) {
		res += (1u << (sft-1));
		res >>= sft;
	}

	if ((sft || do_round) && res > max)
		res = max;

	return res;
}

static inline unsigned int avg4(bool do_round, unsigned int max, int sft,
				unsigned int a)
{
	unsigned int		res;

	if (!do_round)
		res = a / 4;
	else
		res = (a + 2) / 4;

	if (sft) {
		res += (1u << (sft-1));
		res >>= sft;
	}

	if ((sft || do_round) && res > max)
		res = max;

	return res;
}

static inline uint32_t get_row32(void const *addr)
{
	return *(uint32_t*)addr;
}

static inline uint64_t get_row64(void const *addr)
{
	return *(uint32_t*)addr;
}

#define read_row(_dst, _addr) do {				\
		switch (sizeof *(_dst)) {			\
		case 4: *(_dst) = get_row32(_addr); break;	\
		case 8: *(_dst) = get_row64(_addr); break;	\
		default: abort();				\
		}						\
	} while (0)

#if __BYTE_ORDER == __LITTLE_ENDIAN
#  define get_col(_row, _col)			       \
	(((_row) >> ((_col) * (sizeof(_row)/4 * 8))) & \
	 ((1u << (sizeof(_row)/4 * 8)) - 1u))
#else
#  define get_col(_row, _col)			       \
	(((_row) >> ((3 - (_col)) * (sizeof(_row)/4 * 8))) &	\
	 ((1u << (sizeof(_row)/4 * 8)) - 1u))
#endif

#if 1
#  define do_prefetch(_addr, _lvl) do { } while (0)
#else
#  define do_prefetch(_addr, _lvl) do { \
		__builtin_prefetch(((void const *)(_addr)) + 128, 0, (_lvl)); \
	} while (0)
#endif

void bayer2rgb_convert_c_opt(struct image_in const *input,
			     struct image_out const *output,
			     struct image_conversion_info *info)
{
	static unsigned int const	border = 2;
	int				handled = -1;

#define CONVERT_BODY	"convert-c-opt_0.inc.h"

	switch (input->type) {
	case BAYER_GBRG:
#include "convert-c-body-outer.inc.h"
		break;

	case BAYER_RGGB:
	case BAYER_GRBG:
	case BAYER_BGGR:
		set_fallback_reason(info, "c-opt: unsupported input format");
		handled = 0;
		break;

	default:
		abort();
	}

	if (!handled)
		bayer2rgb_convert_dumb(input, output, info);
}
